#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/iterable/stl_interop.h>
#include <vex.tests/mstest/tests.range/tests.range.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace range_stl_boost_slist {

    using namespace boost::assign;

    namespace vi = vex::iterable;

#if _MSC_VER == 1800 && BOOST_VERSION == 105500
#pragma message(__FILE__ " boost::container::slist does not compile with boost 105500 and MSC 1800")
#else
    TEST_CLASS(slist)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.stl.boost.slist")
        END_TEST_CLASS_ATTRIBUTE();

        typedef boost::container::slist<int> container_t;
        typedef container_t::iterator iterator_t;
        typedef container_t::const_iterator const_iterator_t;

        container_t 
            m_container, 
            m_container_same_size_different_values, 
            m_container_empty, 
            m_container_with_one_element;
    public:
        TEST_METHOD_INITIALIZE(set_up)
        {
            container_t().swap(m_container);
            container_t().swap(m_container_same_size_different_values);
            container_t().swap(m_container_empty);
            container_t().swap(m_container_with_one_element);

            push_front(m_container)(1)(2)(3)(4)(5);
            push_front(m_container_same_size_different_values)(6)(7)(8)(9)(5);
            push_front(m_container_with_one_element)(1);
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            container_t().swap(m_container);
            container_t().swap(m_container_same_size_different_values);
            container_t().swap(m_container_empty);
            container_t().swap(m_container_with_one_element);
        }

        TEST_METHOD(concepts_and_traits_are_satisfied)
        {
            typedef decltype(vi::make_range(m_container)) range_t;
            typedef vi::range_traits<range_t> traits_t;

            BOOST_CONCEPT_ASSERT((vi::concept::ForwardRange<range_t>));
            Assert::IsTrue(std::is_same<container_t::reference, traits_t::reference>::value);
            Assert::IsTrue(std::is_same<container_t::value_type, traits_t::value_type>::value);
            Assert::IsTrue(std::is_same<container_t::pointer, traits_t::pointer>::value);
        }

        TEST_METHOD(supports_move_and_swap)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            auto t_r1 = vi::make_range(m_container);
            auto t_r2 = vi::make_range(m_container_same_size_different_values);

            bidirectional_range::has_expected_type_and_size<vi::stl_interop::range_from_iterator<iterator_t>>(
                t_r1, sizeof(vi::stl_interop::range_from_iterator<iterator_t>));
            bidirectional_range::supports_and_executes_swap_correctly(
                m_container, m_container_same_size_different_values, t_r1, t_r2);
        }

        TEST_METHOD(supports_non_const_to_const_conversion_but_not_vice_versa)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            typedef decltype(vi::make_range(
                std::declval<iterator_t>(), std::declval<iterator_t>())) range_t;
            typedef decltype(vi::make_range(
                std::declval<const_iterator_t>(), std::declval<const_iterator_t>())) const_range_t;
            
            Assert::IsTrue(std::is_convertible<range_t, const_range_t>::value);
            Assert::IsFalse(std::is_convertible<const_range_t, range_t>::value);
        }

        TEST_METHOD(move_front_iteration_succeeds_expected_number_of_times)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            bidirectional_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_range(m_container_empty), m_container_empty.size());

            bidirectional_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_range(m_container), m_container.size());

            bidirectional_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_range(m_container.begin(), m_container.end()), m_container.size());

            bidirectional_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_range(m_container.cbegin(), m_container.cend()), m_container.size());
        }

        TEST_METHOD(move_front_succeeds_n_times_on_range_with_n_elements)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            one_pass_range::move_front_succeeds_n_times_on_range_with_n_elements(
                vi::make_range(m_container), m_container.cbegin(), m_container.cend());

            one_pass_range::move_front_succeeds_n_times_on_range_with_n_elements(
                vi::make_range(m_container_empty), m_container_empty.cbegin(), m_container_empty.cend());
        }

        TEST_METHOD(after_calling_move_front_n_times_front_points_to_nth_element)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            for (auto t_size = m_container.size(); t_size > 0; --t_size) {
                one_pass_range::after_calling_move_front_n_times_front_points_to_nth_element(
                    vi::make_range(m_container), t_size, m_container.cbegin(), m_container.cend());
            }
        }

        // forward iterable portion
        TEST_METHOD(save_creates_a_copy_of_range)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            forward_range::save_creates_a_copy_of_current_range(vi::make_range(m_container));
            forward_range::save_creates_a_copy_of_current_range(vi::make_range(m_container_with_one_element));
            forward_range::save_creates_a_copy_of_current_range(vi::make_range(m_container_empty));
        }
    };
#endif
}}